﻿using iTool.Common;
using iTool.PluginCore.Connector;
using iTool.Utils;
using System.Reflection;

namespace iTool.Core
{
    public class ManagerPluginCoreConnector
    {
        private TaskCompletionSource<Task> LoadTaskSource = new TaskCompletionSource<Task>();
        protected IList<HttpWorkerInfo> httpWorkerInfos;
        protected Dictionary<string, RouteTemplate> routeTemplates = new Dictionary<string, RouteTemplate>();
        protected IUrlPathToUrlTemplateMatcher matcher { get; }
        protected iObserverbalFileChange iObserverbal { get; }
        public ManagerPluginCoreConnector()
        {
            this.matcher = new RegExUrlMatcher();
            this.ReLoadHttpController();

            // 监听插件目录的变化
            this.iObserverbal = new iObserverbalFileChange(Path.Combine(AppContext.BaseDirectory, "Plugins"), "*.dll", true, false);
            this.iObserverbal.OnChange += (e, s) =>
            {
                Console.WriteLine("Plugin OnChange, start reload...");
                try
                {
                    this.ReLoadHttpController();
                }
                catch (Exception ex)
                {
                    iPrint.Error(ex.Message);
                }
                Console.WriteLine("Plugin OnChange, start reload success.");
            };
        }

        public async Task<(HttpWorkerInfo worker, List<PlaceholderNameAndValue> parameters)> GetApiWorkerAsync<THttpContext>(THttpContext context, bool isReload = false)
            where THttpContext : AbstractHttpContext
        {
            await this.LoadTaskSource.Task;
            try
            {
                foreach (var worker in this.httpWorkerInfos)
                {
                    if (context.Method.Name == worker.MethodType)
                    {
                        if (this.matcher.Match(context.Path, string.Empty, routeTemplates[worker.RouteTemplate]))
                        {
                            IUrlPathPlaceholderNameAndValueFinder placeholder = new UrlPathPlaceholderNameAndValueFinder();
                            var parameters = placeholder.Find(context.Path, context.QueryString, worker.RouteTemplate);
                            return (worker, parameters);
                        }
                    }
                }
            }
            catch (Exception)
            {
                if (!isReload)
                {
                    return await this.GetApiWorkerAsync(context, true);
                }
            }
            return default;
        }

        private void ReLoadHttpController()
        {
            try
            {
                this.LoadTaskSource = new TaskCompletionSource<Task>();
                // 加载所有Type
                var baseAssemblies = iAssemblyExtension.LoadDirectory("basePlugins", AppContext.BaseDirectory, "iToolPlugin.");
                //var pluginsExtension = iAssemblyExtension.LoadDirectory("plugins", "./Plugins", "iToolPlugin.");
                var pluginsExtension = iAssemblyExtension.LoadDirectory("plugins", Path.Combine(AppContext.BaseDirectory, "Plugins"));
                var assemblies = baseAssemblies.Concat(pluginsExtension).Distinct();
                this.httpWorkerInfos = new List<HttpWorkerInfo>();
                this.routeTemplates = new Dictionary<string, RouteTemplate>();

                var worker = iTypeExtension.GetTypesByAttribute<Route>(assemblies.ToArray());
                IRouteTemplatePatternCreator routeTemplatePatternCreator = new RouteTemplatePatternCreator();

                foreach (var item in worker)
                {
                    var routeAttr = item.GetCustomAttribute<Route>();

                    string routeTemp = routeAttr?.Template;

                    if (!routeTemp.StartsWith("/"))
                    {
                        routeTemp = "/" + routeTemp;
                    }

                    string name = string.Empty;
                    if (item.Name.EndsWith("Worker"))
                    {
                        name = item.Name.Substring(0, item.Name.Length - 6);
                    }

                    routeTemp = string.IsNullOrWhiteSpace(routeTemp) ? name : routeTemp.Replace("{worker}", name);

                    var instance = iTypeExtension.CreateInstanceByType(item, type => iBox.GetService(type, "HttpWorker"));

                    var methods = item.GetMethods();
                    foreach (var method in methods)
                    {
                        var methodAttrs = method.GetCustomAttributes();

                        if (methodAttrs.Count() > 0)
                        {
                            foreach (var attr in methodAttrs)
                            {

                                HttpWorkerInfo httpWorkerInfo = null;

                                switch (attr)
                                {
                                    case var a when a is Get get:
                                        httpWorkerInfo = new HttpWorkerInfo
                                        {
                                            MethodType = "GET",
                                            RouteTemplate = string.IsNullOrWhiteSpace(get.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{get.Template}"
                                        };
                                        break;
                                    case var a when a is Post post:
                                        httpWorkerInfo = new HttpWorkerInfo
                                        {
                                            MethodType = "POST",
                                            RouteTemplate = string.IsNullOrWhiteSpace(post.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{post.Template}"
                                        };
                                        break;
                                    case var a when a is Delete delete:
                                        httpWorkerInfo = new HttpWorkerInfo
                                        {
                                            MethodType = "DELETE",
                                            RouteTemplate = string.IsNullOrWhiteSpace(delete.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{delete.Template}"
                                        };
                                        break;
                                    case var a when a is Put put:
                                        httpWorkerInfo = new HttpWorkerInfo
                                        {
                                            MethodType = "PUT",
                                            RouteTemplate = string.IsNullOrWhiteSpace(put.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{put.Template}"
                                        };
                                        break;
                                }

                                if (httpWorkerInfo != null)
                                {
                                    httpWorkerInfo.Method = method;
                                    httpWorkerInfo.WorkerType = item;
                                    httpWorkerInfo.Instance = instance;

                                    RouteTemplate routeTemplate = routeTemplatePatternCreator.Create(httpWorkerInfo.RouteTemplate);
                                    routeTemplates.Add(httpWorkerInfo.RouteTemplate, routeTemplate);
                                    httpWorkerInfos.Add(httpWorkerInfo);
                                }
                            }
                        }

                    }
                }

            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.LoadTaskSource.TrySetResult(Task.CompletedTask);
            }
        }
    }
}
